package com.qjl.parser.logicflow;

import com.qjl.core.ELNode;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author : zhangrongyan
 * @date : 2023/5/4 9:38
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LfParseContext {
	/**
	 * 根节点
	 */
	private String rootId;

	/**
	 * 节点Id -> 节点的多个边
	 * sourceId-> List<EdgeEntity>
	 */
	Map<String, List<LfEdge>> sourceNodeToEdgesMap;
	Map<String, List<LfEdge>> targetNodeToEdgesMap;

	/**
	 * 所有的节点
	 */
	Map<String, LfNode> nodeMap;

	/**
	 * 所有的when节点
	 */
	Map<String, LfNode> whenNodeMap;


	/**
	 * 最外层的when
	 */
	Set<LfNode> outermostLayerWhen;

	/**
	 * 获取when中的根节点，可能包含when组的id
	 *
	 * @param whenLfNode when节点
	 * @return
	 */
	public Set<String> getRootIdInWhen(LfNode whenLfNode) {
		Map<String, LfNode> nodeMap = this.getNodeMap();
		Set<String> firstIds = this.getFirstIdsInWhen(whenLfNode);
		Set<String> rootIds = new HashSet<>(firstIds);
		//获取when中的when子节点
		Set<String> whenChildren = this.getChildrenWhenInWhen(nodeMap, whenLfNode);

		for (String whenId : whenChildren) {
			LfNode lfNode = nodeMap.get(whenId);
			Set<String> firstIds2 = getFirstIdsInWhen(lfNode);
			if (firstIds.containsAll(firstIds2)) {
				rootIds.removeAll(firstIds2);
				rootIds.add(whenId);
			}
		}
		return rootIds;

	}

	/**
	 * 获取靠前的第一个节点
	 *
	 * @param whenLfNode
	 * @return
	 */
	private Set<String> getFirstIdsInWhen(LfNode whenLfNode) {
		Map<String, List<LfEdge>> sourceNodeToEdgesMap = this.getSourceNodeToEdgesMap();
		Map<String, List<LfEdge>> targetNodeToEdgesMap = this.getTargetNodeToEdgesMap();

		List<String> allChildNodeInWhen = getAllChildNodeInWhen(whenLfNode);
		Set<String> fatherIds = getFatherIds(targetNodeToEdgesMap, allChildNodeInWhen);

		//待确认的rootId节点
		Set<String> readyRootIds = new HashSet<>();

		for (String f : fatherIds) {
			List<LfEdge> lfEdges = sourceNodeToEdgesMap.get(f);
			if (CollectionUtils.isEmpty(lfEdges)) {
				continue;
			}
			Set<String> collect = lfEdges.stream()
					.map(LfEdge::getTargetNodeId)
					.filter(allChildNodeInWhen::contains)
					.collect(Collectors.toSet());
			readyRootIds.addAll(collect);
		}
		return readyRootIds;
	}

	/**
	 * 遍历 when下所有的子when
	 *
	 * @param nodeMap
	 * @param when
	 * @return
	 */
	private Set<String> getChildrenWhenInWhen(Map<String, LfNode> nodeMap, LfNode when) {
		List<String> childrenInWhen = when.getChildren();
		//获取when中的when子节点
		return childrenInWhen.stream().filter(e -> {
			LfNode lfNode = nodeMap.get(e);
			String nodeType = lfNode.getLfNodeProperty(LfNodePropertyEnum.nodeType.name());
			return ELNode.ELNameEnum.WHEN.name().equals(nodeType);
		}).collect(Collectors.toSet());
	}

	/**
	 * 递归析构出所有when内的所有节点
	 *
	 * @param when
	 * @return
	 */
	public List<String> getAllChildNodeInWhen(LfNode when) {
		List<String> children = when.getChildren();
		Map<String, LfNode> nodeMap = this.getNodeMap();

		List<String> res = new ArrayList<>();
		for (String c : children) {
			LfNode lfNode1 = nodeMap.get(c);
			if (lfNode1 == null) {
				continue;
			}
			if (ELNode.ELNameEnum.WHEN.name().equals(lfNode1.getProperties().get(LfNodePropertyEnum.nodeType.name()))) {
				List<String> allChildNodeInWhen = getAllChildNodeInWhen(lfNode1);
				res.addAll(allChildNodeInWhen);
			} else {
				res.add(c);
			}
		}

		return res;
	}

	/**
	 * 获取父亲id
	 *
	 * @param targetNodeToEdgesMap 目标节点来边缘Map
	 * @param children             孩子们
	 * @return {@link Set}<{@link String}>
	 */
	private Set<String> getFatherIds(Map<String, List<LfEdge>> targetNodeToEdgesMap, List<String> children) {
		//获取他们所有的父节点
		Set<String> fatherIds = new HashSet<>();
		for (String c : children) {
			List<LfEdge> lfEdges = targetNodeToEdgesMap.get(c);
			if (CollectionUtils.isEmpty(lfEdges)) {
				continue;
			}
			Set<String> sourceIds = lfEdges.stream().map(LfEdge::getSourceNodeId).collect(Collectors.toSet());
			fatherIds.addAll(sourceIds);
		}
		//删除后，剩下就是他们的根
		children.forEach(fatherIds::remove);
		return fatherIds;
	}


	/**
	 * 寻找一个whenId,包含任意一个指定的targetIds组件
	 *
	 * @param childrenInWhen
	 * @param targetIds
	 * @return
	 */
	public String findWhenContainAnyOne(Set<String> childrenInWhen, List<String> targetIds) {
		Map<String, LfNode> nodeMap = this.getNodeMap();
		for (String whenId : childrenInWhen) {
			LfNode lfNode = nodeMap.get(whenId);
			if (lfNode == null) {
				continue;
			}
			List<String> children = lfNode.getChildren();
			//先看当前范围内是否有包含任意一个targetIds
			for (String targetId : targetIds) {
				if (children.contains(targetId)) {
					return whenId;
				}
			}

			//再递归看，下面子when是否包含
			Set<String> childrenWhenInWhen = this.getChildrenWhenInWhen(nodeMap, lfNode);
			if (!CollectionUtils.isEmpty(childrenWhenInWhen)) {
				String whenContainAnyOne = this.findWhenContainAnyOne(childrenWhenInWhen, targetIds);
				if (!StringUtils.isEmpty(whenContainAnyOne)) {
					return whenId;
				}
			}

		}
		return null;
	}


	/**
	 * 递归获取下面所有的子when
	 *
	 * @param when
	 * @return
	 */
	public Set<String> recursiveWhenGetWhens(LfNode when) {
		Map<String, LfNode> nodeMap = this.getNodeMap();
		Set<String> childrenWhenInWhen = this.getChildrenWhenInWhen(nodeMap, when);
		if (CollectionUtils.isEmpty(childrenWhenInWhen)) {
			return Collections.emptySet();
		}
		Set<String> res = new HashSet<>();
		res.addAll(childrenWhenInWhen);
		for (String whenChild : childrenWhenInWhen) {
			LfNode lfNode = nodeMap.get(whenChild);
			Set<String> strings = this.recursiveWhenGetWhens(lfNode);
			res.addAll(strings);
		}
		return res;
	}

}
